package com.xc.file.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.baomidou.mybatisplus.extension.toolkit.SqlHelper;
import com.xc.config.aspect.AuthorityHandle;
import com.xc.core.bean.QueryBean;
import com.xc.core.dto.FileDto;
import com.xc.core.dto.UploadDto;
import com.xc.core.enums.RedisPrefix;
import com.xc.core.enums.RedisTime;
import com.xc.core.utils.RedisUtils;
import com.xc.core.utils.ServiceUtils;
import com.xc.file.bean.*;
import com.xc.file.config.Constants;
import com.xc.file.dto.FolderDto;
import com.xc.file.dto.FolderFileDto;
import com.xc.file.entity.FileEntity;
import com.xc.file.entity.FileHashEntity;
import com.xc.file.entity.FolderEntity;
import com.xc.file.entity.ShareEntity;
import com.xc.file.enums.FailCode;
import com.xc.file.enums.FileSuffix;
import com.xc.file.function.FolderHandle;
import com.xc.file.mapper.FileHashMapper;
import com.xc.file.mapper.FileMapper;
import com.xc.file.mapper.FolderMapper;
import com.xc.file.mapper.ShareMapper;
import com.xc.file.model.DownloadModel;
import com.xc.file.model.FolderFileModel;
import com.xc.file.model.ShareIdModel;
import com.xc.file.service.*;
import com.xc.tool.utils.FileUtils;
import com.xc.tool.utils.Md5Utils;
import com.xc.tool.utils.ObjectUtils;
import com.xc.tool.utils.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.client.RestTemplate;

import java.io.*;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import java.util.zip.CRC32;
import java.util.zip.CheckedOutputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * <p>文件夹服务实现类</p>
 *
 * @author xc
 * @version v1.0
 */
@Service
@Slf4j
public class FolderServiceImpl extends ServiceImpl<FolderMapper, FolderEntity> implements FolderService {

    @Autowired
    private FolderMapper folderMapper;
    @Autowired
    private FileService fileService;
    @Autowired
    private FileMapper fileMapper;
    @Autowired
    private Constants constants;
    @Autowired
    private RestTemplate restTemplate;
    @Autowired
    private UploadService uploadService;
    @Autowired
    private ShareService shareService;
    @Autowired
    private FileCacheService fileCacheService;
    @Autowired
    private FileHashMapper fileHashMapper;
    @Autowired
    private UserInfoService userInfoService;
    @Autowired
    private ShareMapper shareMapper;
    @Autowired
    private FolderService folderService;


    @Override
    public synchronized FolderEntity createParentFolders(String parentNode, String userId, String path) {
        FolderEntity entity = null;
        // 创建文件夹
        String[] folderPaths = FileUtils.getFolderPaths(path != null ? path : "");
        // 验证文件夹是否存在
        for (String folderName : folderPaths) {
            FolderEntity folderEntity = new FolderEntity();
            folderEntity.setParentNode(parentNode);
            folderEntity.setUserId(userId);
            folderEntity.setName(folderName);
            FolderEntity folder = folderMapper.selectOne(new QueryWrapper<>(folderEntity));
            if (folder == null) {
                // 创建文件夹
                try {
                    folder = new FolderEntity();
                    folder.setParentNode(parentNode);
                    folder.setUserId(userId);
                    folder.setName(folderName);
                    folder.setNode(IdWorker.getIdStr());
                    if (!this.retBool(folderMapper.insert(folder))) {
                        throw FailCode.CREATE_FOLDER_FAIL.getOperateException();
                    }
                } catch (DuplicateKeyException e) {
                    throw FailCode.FOLDER_EXIST.getOperateException();
                }
            }
            parentNode = folder.getNode();
            entity = folder;
        }
        return entity;
    }

    @Override
    public FolderEntity createFolder(String userId, String folderId, List<String> userIds, FolderBean folderBean) {
        if (folderBean.getName() == null || folderBean.getName().equals("")) {
            throw FailCode.FOLDER_NAME_NOT_NULL.getOperateException();
        }
        FolderEntity folder = null;
        if (folderId != null && !"".equals(folderId)) {
            // 查询父级文件夹
            folder = folderMapper.selectById(folderId);
            // 验证父级文件夹是否存在
            if (folder == null) {
                throw FailCode.PARENT_FOLDER_NOT_EXIST.getOperateException();
            }
            // 验证父级文件夹是不是当前用户的
            if (!AuthorityHandle.verifyData(userIds, folder.getUserId())) {
                throw FailCode.NOT_DATA_AUTHORITY.getOperateException();
            }
            userId = folder.getUserId();
        }
        // 判断当前文件夹是否重复，重复重命名
        FolderEntity folderEntity = new FolderEntity();
        if (folder == null) {
            folderEntity.setParentNode(constants.getRoot());
        } else {
            folderEntity.setParentNode(folder.getNode());
        }
        folderEntity.setName(folderBean.getName());
        folderEntity.setUserId(userId);
        FolderEntity entity = folderMapper.selectOne(new QueryWrapper<>(folderEntity));
        if (entity != null) {
            //不等于空，重命名文件
            String name = FileUtils.rename(entity.getName());
            folderEntity.setName(name);
        }
        // 创建文件夹
        try {
            folderEntity.setNode(IdWorker.getIdStr());
            if (!this.retBool(folderMapper.insert(folderEntity))) {
                throw FailCode.CREATE_FOLDER_FAIL.getOperateException();
            }
        } catch (DuplicateKeyException e) {
            throw FailCode.FOLDER_EXIST.getOperateException();
        }
        return folderEntity;
    }

    @Override
    public FolderDto updateFolder(List<String> userIds, String folderId, UpdateFolderBean updateFolderBean) {
        FolderEntity folderEntity = folderMapper.selectById(folderId);
        if (folderEntity == null) {
            throw FailCode.FOLDER_NOT_EXIST.getOperateException();
        }
        // 验证数据权限
        if (!AuthorityHandle.verifyData(userIds, folderEntity.getUserId())) {
            throw FailCode.NOT_DATA_AUTHORITY.getOperateException();
        }
        // 修改文件夹
        try {
            folderEntity.setName(updateFolderBean.getName());
            if (!this.updateById(folderEntity)) {
                throw FailCode.UPDATE_FOLDER_FAIL.getOperateException();
            }
        } catch (DuplicateKeyException e) {
            throw FailCode.FOLDER_EXIST.getOperateException();
        }
        return ObjectUtils.convert(new FolderDto(), folderEntity);
    }

    @Override
    @Transactional
    public void deleteUserFolder(List<String> userIds, String folderId) {
        //获取文件夹信息
        FolderEntity folderEntity = folderMapper.selectById(folderId);
        //验证文件夹信息是否存在
        if (folderEntity == null) {
            throw FailCode.FOLDER_NOT_EXIST.getOperateException();
        }
        // 验证数据权限
        if (!AuthorityHandle.verifyData(userIds, folderEntity.getUserId())) {
            throw FailCode.NOT_DATA_AUTHORITY.getOperateException();
        }
        // 查询所有子集文件夹集合
        List<FolderEntity> folders = getChildrenFolder(folderEntity.getUserId(), folderEntity.getNode());
        folders.add(folderEntity);
        if (folders.size() > 0) {
            String userId = folders.get(0).getUserId();
            List<String> folderIds = folders.stream().map(FolderEntity::getId).collect(Collectors.toList());
            if (folderIds.size() > 0) {
                // 删除文件
                List<FileEntity> files = fileService.getFilesByFolderIds(userId, folderIds);
                if (files.size() > 0) {
                    List<String> fileIds = files.stream().map(FileEntity::getId).collect(Collectors.toList());
                    if (!retBool(fileMapper.deleteBatchIds(fileIds))) {
                        throw FailCode.DELETE_FILE_FAIL.getOperateException();
                    }
                }
                // 删除文件夹
                if (!retBool(folderMapper.deleteBatchIds(folderIds))) {
                    throw FailCode.DELETE_FOLDER_FAIL.getOperateException();
                }
            }
        }
    }

    @Override
    @Transactional
    public FolderFileDto batchCopyFolderFile(String userId, List<String> userIds, FolderFileBean folderFileBean) {
        // 查询目标文件夹
        FolderEntity targetFolder = getTargetFolder(userId, folderFileBean.getFolderId());
        // 查询所有文件夹
        FolderFileModel folderFileModel = getFolderFiles(folderFileBean.getFolderIds(), folderFileBean.getFileIds(), null);
        // 验证父级文件夹是否正确，验证目标文件夹是否存在
        verifyRoot(userIds, targetFolder, folderFileModel);
        // 复制文件目录
        return batchCopyFolderFile(targetFolder, folderFileModel, null);
    }

    @Override
    @Transactional
    public FolderFileDto batchMoveFolderFile(String userId, FolderFileBean folderFileBean) {
        // 查询目标文件夹
        FolderEntity targetFolder = getTargetFolder(userId, folderFileBean.getFolderId());
        // 不能向他人文件夹移动
        if (!targetFolder.getUserId().equals(userId)) {
            throw FailCode.NOT_TOWARDS_OTHERS_FOLDER_MOVE.getOperateException();
        }
        // 查询所有文件夹
        FolderFileModel folderFileModel = getFolderFiles(folderFileBean.getFolderIds(), folderFileBean.getFileIds(), null);
        // 验证父级文件夹是否正确，验证目标文件夹是否存在
        verifyRoot(null, targetFolder, folderFileModel);
        // 获取所有要复制的文件夹
        FolderFileDto folderFileDto = new FolderFileDto();
        // 移动目录
        for (FolderEntity entity : folderFileModel.getRootFolders()) {
            // 验证不能向下级移动
            if (targetFolder.getId() != null && !targetFolder.getId().equals(constants.getRoot())) {
                List<FolderEntity> entities = getParentFolderList(targetFolder.getId(), null);
                for (FolderEntity folderEntity : entities) {
                    if (folderEntity.getId().equals(entity.getId())) {
                        throw FailCode.CANNOT_DOWN_MOVE.getOperateException();
                    }
                }
            }
            // 验证文件夹是否是自己的
            if (!userId.equals(entity.getUserId())) {
                throw FailCode.NOT_MOVE_OTHERS_FOLDER.getOperateException();
            }
            entity.setParentNode(targetFolder.getNode());
            // 移动文件夹
            if (!updateById(entity)) {
                throw FailCode.COPY_FOLDER_FAIL.getOperateException();
            }
        }
        folderFileDto.setFolders(ObjectUtils.convertList(folderFileModel.getRootFolders(), FolderDto.class));
        // 移动文件
        for (FileEntity entity : folderFileModel.getRootFiles()) {
            // 验证文件是否是自己的
            if (!userId.equals(entity.getUserId())) {
                throw FailCode.NOT_MOVE_OTHERS_FILE.getOperateException();
            }
            entity.setFolderId(targetFolder.getId());
            if (!retBool(fileMapper.updateById(entity))) {
                throw FailCode.COPY_FILE_FAIL.getOperateException();
            }
        }
        folderFileDto.setFiles(ObjectUtils.convertList(folderFileModel.getRootFiles(), FileDto.class));
        return folderFileDto;

    }

    @Override
    @Transactional
    public FolderFileDto batchSaveShareFolderFile(String userId, String shareId, FolderFileBean folderFileBean) {
        // 查询目标文件夹
        FolderEntity targetFolder = getTargetFolder(userId, folderFileBean.getFolderId());
        // 不能向他人文件夹保存
        if (!targetFolder.getUserId().equals(userId)) {
            throw FailCode.NOT_TOWARDS_OTHERS_FOLDER_SAVE.getOperateException();
        }
        // 获取共享的文件夹/文件信息
        ShareIdModel shareIdModel = shareService.getShareIdModelByShareId(shareId);
        //查询共享文件信息，修改保存次数
        ShareEntity shareEntity = shareIdModel.getShareEntity();
        shareEntity.setPreserveNum(shareEntity.getPreserveNum() + 1);
        if (!this.retBool(shareMapper.updateById(shareEntity))) {
            throw FailCode.UPDATE_SHARE_FILE_FAIL.getOperateException();
        }
        // 获取根文件夹下的文件夹
        FolderFileModel folderFileModel = getFolderFiles(folderFileBean.getFolderIds(), folderFileBean.getFileIds(), shareIdModel);
        // 验证父级文件夹是否正确，验证目标文件夹是否存在
        verifyRoot(null, targetFolder, folderFileModel);
        // 复制目录
        return batchCopyFolderFile(targetFolder, folderFileModel, shareIdModel);
    }

    @Override
    public FolderEntity getTargetFolder(String userId, String folderId) {
        FolderEntity targetFolder;
        if (folderId == null || "".equals(folderId)) {
            targetFolder = new FolderEntity();
            targetFolder.setUserId(userId);
            targetFolder.setId(constants.getRoot());
            targetFolder.setNode(constants.getRoot());
        } else {
            targetFolder = folderMapper.selectById(folderId);
            if (targetFolder == null) {
                throw FailCode.TARGET_FOLDER_NOT_NULL.getOperateException();
            }
        }
        return targetFolder;
    }


    /**
     * 批量复制文件夹及文件
     *
     * @param targetFolder    目标文件夹
     * @param folderFileModel 目录文件信息
     * @return 文件及文件夹信息
     */
    private FolderFileDto batchCopyFolderFile(FolderEntity targetFolder, FolderFileModel folderFileModel, ShareIdModel shareIdModel) {
        FolderFileDto folderFileDto = new FolderFileDto();
        // 复制文件夹
        for (FolderEntity entity : folderFileModel.getRootFolders()) {
            String oldNode = entity.getNode();
            String newNode = IdWorker.getIdStr();
            // 创建下级
            getChildrenFolder(entity.getUserId(), oldNode, newNode, (folderList, parentNewNode) -> {
                // 处理文件夹
                if (shareIdModel != null) {
                    // 保存共享文件处理
                    for (FolderEntity folderEntity : folderList) {
                        for (String folderId : shareIdModel.getFolderIds()) {
                            if (folderEntity.getId().equals(folderId)) {
                                String node = IdWorker.getIdStr();
                                createFolderFile(targetFolder.getUserId(), parentNewNode, node, folderEntity, shareIdModel);
                            }
                        }
                    }
                } else {
                    // 复制文件夹处理
                    for (FolderEntity folderEntity : folderList) {
                        String node = IdWorker.getIdStr();
                        createFolderFile(targetFolder.getUserId(), parentNewNode, node, folderEntity, null);
                    }
                }
            });
            // 创建文件夹及文件
            createFolderFile(targetFolder.getUserId(), targetFolder.getNode(), newNode, entity, shareIdModel);
        }
        folderFileDto.setFolders(ObjectUtils.convertList(folderFileModel.getRootFolders(), FolderDto.class));
        // 复制文件
        fileService.batchCopyFile(targetFolder.getUserId(), targetFolder.getId(), folderFileModel.getRootFiles());
        folderFileDto.setFiles(ObjectUtils.convertList(folderFileModel.getRootFiles(), FileDto.class));
        // 验证网盘空间是否足够
        userInfoService.verifyUserSpace(targetFolder.getUserId(), 0);
        return folderFileDto;
    }

    /**
     * 创建文件夹及文件
     *
     * @param userId       用户id
     * @param targetNode   目标父级文件夹节点
     * @param entity       文件夹实体
     * @param shareIdModel 共享文件信息
     */
    private void createFolderFile(String userId, String targetNode, String newNode, FolderEntity entity, ShareIdModel shareIdModel) {
        // 复制文件夹
        String folderId = entity.getId();
        entity.setId(null);
        entity.setUpdateTime(null);
        entity.setCreateTime(null);
        entity.setVersion(0);
        entity.setUserId(userId);
        entity.setNode(newNode);
        entity.setParentNode(targetNode);
        if (!save(entity)) {
            throw FailCode.COPY_FOLDER_FAIL.getOperateException();
        }
        // 查询文件
        FileEntity fileEntity = new FileEntity();
        fileEntity.setFolderId(folderId);
        List<FileEntity> fileList = fileMapper.selectList(new QueryWrapper<>(fileEntity));
        // 处理文件
        List<FileEntity> files = new ArrayList<>();
        if (shareIdModel != null) {
            for (String fileId : shareIdModel.getFileIds()) {
                for (FileEntity file : fileList) {
                    if (file.getId().equals(fileId)) {
                        files.add(file);
                    }
                }
            }
        } else {
            files.addAll(fileList);
        }
        // 复制文件
        fileService.batchCopyFile(userId, entity.getId(), files);
    }


    /**
     * 验证父级文件夹是否正确
     * 验证目标文件夹是否存在
     *
     * @param userIds         用户id集合
     * @param targetFolder    目标文件夹
     * @param folderFileModel 文件夹及文件信息
     */
    private void verifyRoot(List<String> userIds, FolderEntity targetFolder, FolderFileModel folderFileModel) {
        // 验证根文件夹
        if (folderFileModel.getRootFolders().size() > 0) {
            String parentNode = null;
            for (FolderEntity folderEntity : folderFileModel.getRootFolders()) {
                if (parentNode == null) {
                    parentNode = folderEntity.getParentNode();
                }
                // 验证待移动的文件夹父级是否相同
                if (!parentNode.equals(folderEntity.getParentNode())) {
                    throw FailCode.PARENT_FOLDER_ERROR.getOperateException();
                }
                if (userIds != null) {
                    // 验证是否有操作权限
                    if (!AuthorityHandle.verifyData(userIds, folderEntity.getUserId())) {
                        throw FailCode.NOT_DATA_AUTHORITY.getOperateException();
                    }
                }
                // 验证文件夹是否存在
                FolderEntity folder = new FolderEntity();
                folder.setParentNode(targetFolder.getNode());
                folder.setName(folderEntity.getName());
                folder.setUserId(targetFolder.getUserId());
                FolderEntity entity = folderMapper.selectOne(new QueryWrapper<>(folder));
                if (entity != null) {
                    throw FailCode.FOLDER_EXIST.getOperateException();
                }
            }
        }
        // 验证根文件
        if (folderFileModel.getRootFiles().size() > 0) {
            String folderId = null;
            for (FileEntity fileEntity : folderFileModel.getRootFiles()) {
                if (userIds != null) {
                    // 验证是否有操作权限
                    if (!AuthorityHandle.verifyData(userIds, fileEntity.getUserId())) {
                        throw FailCode.NOT_DATA_AUTHORITY.getOperateException();
                    }
                }
                if (folderId == null) {
                    folderId = fileEntity.getFolderId();
                }
                // 验证待移动的文件夹父级是否相同
                if (!folderId.equals(fileEntity.getFolderId())) {
                    throw FailCode.PARENT_FOLDER_ERROR.getOperateException();
                }

                // 验证文件是否存在
                FileEntity entity = new FileEntity();
                entity.setFolderId(targetFolder.getId());
                entity.setName(fileEntity.getName());
                entity.setUserId(targetFolder.getUserId());
                FileEntity file = fileService.getOne(new QueryWrapper<>(entity));
                if (file != null) {
                    throw FailCode.FILE_EXIST.getOperateException();
                }
            }
        }
    }

    @Override
    public List<FolderEntity> getChildrenFolder(String userId, String parentNode) {
        return getChildrenFolder(userId, parentNode, null, null);
    }

    @Override
    public List<FolderEntity> getChildrenFolder(String userId, String parentNode, String parentNewNode, FolderHandle<FolderEntity> action) {
        FolderEntity folderEntity = new FolderEntity();
        folderEntity.setUserId(userId);
        folderEntity.setParentNode(parentNode);
        List<FolderEntity> folders = folderMapper.selectList(new QueryWrapper<>(folderEntity));
        List<String> newNodes = new ArrayList<>();
        if (action != null) {
            List<FolderEntity> entities = ObjectUtils.convertList(folders, FolderEntity.class);
            action.handle(entities, parentNewNode);
            newNodes.addAll(entities.stream().map(FolderEntity::getNode).collect(Collectors.toList()));
        }
        int index = 0;
        while (index < folders.size()) {
            FolderEntity entity = folders.get(index);
            String newNode = null;
            if (action != null) {
                newNode = newNodes.get(index);
            }
            FolderEntity folder = new FolderEntity();
            folder.setUserId(userId);
            folder.setParentNode(entity.getNode());
            List<FolderEntity> folderList = folderMapper.selectList(new QueryWrapper<>(folder));
            if (folderList.size() > 0) {
                if (action != null) {
                    List<FolderEntity> entities = ObjectUtils.convertList(folderList, FolderEntity.class);
                    action.handle(entities, newNode);
                    newNodes.addAll(entities.stream().map(FolderEntity::getNode).collect(Collectors.toList()));
                }
                folders.addAll(folderList);
            }
            index++;
        }
        return folders;
    }

    @Override
    public FolderEntity getFolderByPath(String userId, String path) {
        if (path.contains("/")) {
            String[] folderPaths = FileUtils.getFolderPaths(path);
            String parentNode = constants.getRoot();
            FolderEntity folderEntity = null;
            for (String folderName : folderPaths) {
                FolderEntity entity = new FolderEntity();
                entity.setUserId(userId);
                entity.setParentNode(parentNode);
                entity.setName(folderName);
                folderEntity = folderMapper.selectOne(new QueryWrapper<>(entity));
                if (folderEntity == null) {
                    throw FailCode.FOLDER_PATH_ERROR.getOperateException();
                }
                parentNode = folderEntity.getNode();
            }
            if (folderEntity == null) {
                throw FailCode.FOLDER_PATH_ERROR.getOperateException();
            }
            return folderEntity;
        } else {
            throw FailCode.FOLDER_PATH_ERROR.getOperateException();
        }
    }

    @Override
    public FolderFileModel getFolderFiles(List<String> folderIds, List<String> fileIds, ShareIdModel shareIdModel) {
        List<String> rootFolders = new ArrayList<>();
        List<String> rootFiles = new ArrayList<>();
        if (shareIdModel != null) {
            List<String> shareFolderIds = shareIdModel.getRootFolderIds();
            shareFolderIds.addAll(shareIdModel.getFolderIds());
            for (String folderId : folderIds) {
                for (String shareFolderId : shareFolderIds) {
                    if (folderId.equals(shareFolderId)) {
                        rootFolders.add(shareFolderId);
                    }
                }
            }
            List<String> shareFileIds = shareIdModel.getRootFileIds();
            shareFileIds.addAll(shareIdModel.getFileIds());
            for (String fileId : fileIds) {
                for (String shareFileId : shareFileIds) {
                    if (fileId.equals(shareFileId)) {
                        rootFiles.add(shareFileId);
                    }
                }
            }
        } else {
            rootFolders.addAll(folderIds);
            rootFiles.addAll(fileIds);
        }
        FolderFileModel folderFileModel = new FolderFileModel();
        // 获取根文件夹
        if (rootFolders.size() > 0) {
            folderFileModel.setRootFolders(folderMapper.selectBatchIds(rootFolders));
        } else {
            folderFileModel.setRootFolders(new ArrayList<>());
        }
        // 获取根文件
        if (rootFiles.size() > 0) {
            folderFileModel.setRootFiles(fileMapper.selectBatchIds(rootFiles));
        } else {
            folderFileModel.setRootFiles(new ArrayList<>());
        }
        // 查询子集数据
        return folderFileModel;
    }

    @Override
    public List<FolderDto> getUserFolderList(List<String> userIds, QueryBean queryBean, FolderEntity folderEntity) {
        //查询文件夹
        QueryWrapper<FolderEntity> queryWrapper = ServiceUtils.queryData(queryBean, folderEntity);
        queryWrapper.in("user_id", userIds);
        List<FolderEntity> folderEntities = folderMapper.selectList(queryWrapper);
        return ObjectUtils.convertList(folderEntities, FolderDto.class);
    }

    @Override
    public FileDto packFolderFiles(String userId, PackFileBean packFileBean, String token) {
        if (packFileBean.getName() == null || "".equals(packFileBean.getName())) {
            throw FailCode.PACK_NAME_NOT_NULL.getOperateException();
        }
        // 查询目标文件夹
        FolderEntity targetFolder = getTargetFolder(userId, packFileBean.getFolderId());
        // 不能向他人文件夹保存
        if (!targetFolder.getUserId().equals(userId)) {
            throw FailCode.NOT_TOWARDS_OTHERS_FOLDER_SAVE.getOperateException();
        }
        // 查询所有文件
        List<FolderEntity> folders = new ArrayList<>();
        List<FileEntity> files = new ArrayList<>();
        addFolderFile(folders, files, packFileBean.getFolderIds(), packFileBean.getFileIds(), userId);
        //计算出目录的大小
        long size = 0;
        for (FileEntity entity : files) {
            size += entity.getSize();
        }
        // 验证文件大小是否存的下
        userInfoService.verifyUserSpace(userId, size);
        // 检测本服务器空间是否支持打包，不支持调用其他服务器接口打包
        String path = uploadService.getFilePath(size);
        if (path == null) {
            String serviceIp = fileCacheService.getServiceIp(size);
            String url = serviceIp + "/current_user/folder_file/pack";
            HttpHeaders requestHeaders = new HttpHeaders();
            requestHeaders.add("token", token);
            HttpEntity<PackFileBean> requestEntity = new HttpEntity<>(packFileBean, requestHeaders);
            ResponseEntity<FileDto> responseEntity = restTemplate.exchange(url, HttpMethod.POST, requestEntity, FileDto.class);
            return responseEntity.getBody();
        }
        String fileName = StringUtils.generateOnlyId(constants.getMachineId());
        String tempPath = path + File.separator + fileName + FileSuffix.PACK.getSuffix();
        try (OutputStream outputStream = new FileOutputStream(new File(tempPath));
             CheckedOutputStream cos = new CheckedOutputStream(outputStream, new CRC32());
             ZipOutputStream zipStream = new ZipOutputStream(cos)) {
            // 设置下载url
            List<FileDto> dtoList = ObjectUtils.convertList(files, FileDto.class);
            List<FileDto> fileDtoList = fileService.setDownloadUrl(dtoList, "/user/" + userId + "/file/");
            // 设置为有效允许下载
            DownloadModel downloadModel = new DownloadModel();
            downloadModel.setFiles(files);
            String key = RedisPrefix.USER_DOWNLOAD.getKey() + userId;
            RedisUtils.set(key, downloadModel, RedisTime.USER_DOWNLOAD_CACHE.getTime());
            // 打包文件夹
            for (FolderEntity folderEntity : folders) {
                String folderPath = getPackPath(folders, folderEntity.getId());
                ZipEntry entry = new ZipEntry(folderPath.replace("/", File.separator) + "/");
                zipStream.putNextEntry(entry);
            }
            // 打包文件
            for (FileDto fileDto : fileDtoList) {
                String folderPath = "";
                if (folders.size() > 0) {
                    folderPath = getPackPath(folders, fileDto.getFolderId());
                }
                String filePath = folderPath.equals("") ?
                        fileDto.getName() :
                        folderPath.replace("/", File.separator) + "/" + fileDto.getName();
                ZipEntry entry = new ZipEntry(filePath);
                zipStream.putNextEntry(entry);
                httpDownloadFile(zipStream, fileDto, token);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        // 创建文件hash信息
        File tempFile = new File(tempPath);
        String hashCode = Md5Utils.getFileMd5(tempFile);
        FileHashEntity entity = new FileHashEntity();
        entity.setHashCode(hashCode);
        FileHashEntity fileHash = fileHashMapper.selectOne(new QueryWrapper<>(entity));
        if (fileHash == null) {
            fileHash = new FileHashEntity();
            fileHash.setSize(tempFile.length());
            fileHash.setHashCode(hashCode);
            fileHash.setServerUrl(constants.getLocalUrl());
            fileHash.setPath(path);
            if (!SqlHelper.retBool(fileHashMapper.insert(fileHash))) {
                throw FailCode.CREATE_FILE_HASH_FAIL.getOperateException();
            }
        }
        //重命名文件
        String newPath = path + File.separator + hashCode + FileSuffix.SUCCESS.getSuffix();
        File newFile = new File(newPath);
        uploadService.rename(tempFile, newFile, hashCode);
        // 上传成功
        UploadDto uploadDto = new UploadDto();
        uploadDto.setFinish(true);
        uploadDto.setHashCode(hashCode);
        FileBean fileBean = new FileBean();
        fileBean.setName(packFileBean.getName() + ".zip");
        fileBean.setHashCode(hashCode);
        fileBean.setSize(fileHash.getSize());
        fileBean.setFolderId(targetFolder.getId() != null ? targetFolder.getId() : constants.getRoot());
        FileEntity fileEntity = fileService.createFile(userId, fileBean, false);
        return ObjectUtils.convert(new FileDto(), fileEntity);
    }

    /**
     * 获取集合里面的文件夹
     *
     * @param folders  文件夹信息
     * @param folderId 文件夹id
     * @param node     节点
     * @return 文件夹
     */
    private FolderEntity getListFolder(List<FolderEntity> folders, String folderId, String node) {
        for (FolderEntity folderEntity : folders) {
            if (folderId != null) {
                if (folderId.equals(folderEntity.getId())) {
                    return folderEntity;
                }
            }
            if (node != null) {
                if (node.equals(folderEntity.getNode())) {
                    return folderEntity;
                }
            }
        }
        return null;
    }

    /**
     * 打包文件的路径
     *
     * @param folders  文件夹信息
     * @param folderId 文件夹id
     * @return 文件路径
     */
    private String getPackPath(List<FolderEntity> folders, String folderId) {
        StringBuilder path = new StringBuilder();
        String node = constants.getRoot();
        if (!folderId.equals(constants.getRoot())) {
            FolderEntity folderEntity = getListFolder(folders, folderId, null);
            if (folderEntity == null) {
                throw FailCode.FOLDER_NOT_EXIST.getOperateException();
            }
            node = folderEntity.getParentNode();
            path.append(folderEntity.getName());
        }
        while (true) {
            FolderEntity entity = getListFolder(folders, null, node);
            if (entity == null) {
                break;
            }
            node = entity.getParentNode();
            path.insert(0, entity.getName() + "/");
        }
        return path.toString();
    }

    @Override
    public String getFolderSize(String folderId, List<String> shareFileIds) {
        FolderEntity folderEntity = folderMapper.selectById(folderId);
        List<FolderEntity> folders = getChildrenFolder(folderEntity.getUserId(), folderEntity.getNode());
        folders.add(folderEntity);
        BigDecimal bigDecimal = new BigDecimal("0.00");
        if (folders.size() > 0) {
            String userId = folders.get(0).getUserId();
            List<String> folderIds = folders.stream().map(FolderEntity::getId).collect(Collectors.toList());
            List<FileEntity> files = fileService.getFilesByFolderIds(userId, folderIds);
            for (FileEntity fileEntity : files) {
                if (shareFileIds == null) {
                    bigDecimal = bigDecimal.add(new BigDecimal(fileEntity.getSize()));
                } else {
                    for (String shareFileId : shareFileIds) {
                        if (shareFileId.equals(fileEntity.getId())) {
                            bigDecimal = bigDecimal.add(new BigDecimal(fileEntity.getSize()));
                        }
                    }
                }
            }
        }
        return bigDecimal.toString();
    }

    @Override
    public String getPathByFolderId(String folderId) {
        if (constants.getRoot().equals(folderId)) {
            return "";
        }
        FolderEntity folderEntity = folderMapper.selectById(folderId);
        return getPathByFolder(folderEntity);
    }

    @Override
    public String getPathByFolder(FolderEntity folderEntity) {
        StringBuilder path = new StringBuilder(folderEntity.getName());
        String node = folderEntity.getParentNode();
        while (true) {
            FolderEntity folder = new FolderEntity();
            folder.setNode(node);
            FolderEntity entity = folderMapper.selectOne(new QueryWrapper<>(folder));
            if (entity == null) {
                break;
            }
            node = entity.getParentNode();
            path.insert(0, entity.getName() + "/");
        }
        return "/" + path.toString();
    }

    @Override
    public List<FolderEntity> getParentFolderList(String folderId, List<String> folderIds) {
        List<FolderEntity> folders = new ArrayList<>();
        // 查文件夹
        FolderEntity folderEntity1 = new FolderEntity();
        folderEntity1.setId(folderId);
        QueryWrapper<FolderEntity> queryWrapper = new QueryWrapper<>(folderEntity1);
        if (folderIds != null) {
            queryWrapper.in("id", folderIds);

        }
        FolderEntity folderEntity = folderMapper.selectOne(queryWrapper);
        if (folderEntity == null) {
            throw FailCode.FOLDER_NOT_EXIST.getOperateException();
        }
        // 处理数据
        folders.add(folderEntity);
        String node = folderEntity.getParentNode();
        while (true) {
            FolderEntity folder = new FolderEntity();
            folder.setNode(node);
            QueryWrapper<FolderEntity> queryWrapper1 = new QueryWrapper<>(folder);
            if (folderIds != null) {
                queryWrapper1.in("id", folderIds);
            }
            FolderEntity entity = folderMapper.selectOne(queryWrapper1);
            if (entity == null) {
                break;
            }
            node = entity.getParentNode();
            folders.add(entity);
        }
        Collections.reverse(folders);
        return folders;
    }

    @Override
    public void addFolderFile(List<FolderEntity> folders, List<FileEntity> files, List<String> folderIds, List<String> fileIds, String userId) {
        // 查询所有文件
        if (folderIds.size() > 0) {
            List<FolderEntity> entities = folderMapper.selectBatchIds(folderIds);
            for (FolderEntity entity : entities) {
                folders.add(entity);
                folders.addAll(folderService.getChildrenFolder(userId, entity.getNode()));
            }
        }
        // 查询所有文件
        List<String> ids = folders.stream().map(FolderEntity::getId).collect(Collectors.toList());
        if (ids.size() > 0) {
            files.addAll(fileService.getFilesByFolderIds(userId, ids));
        }
        if (fileIds.size() > 0) {
            files.addAll(fileMapper.selectBatchIds(fileIds));
        }
    }

    /**
     * 发送http 请求下载文件打包
     *
     * @param zipStream 打包输出流
     * @param fileDto   文件信息
     */
    private void httpDownloadFile(ZipOutputStream zipStream, FileDto fileDto, String token) {
        String filePath = uploadService.verifyFileExist(fileDto.getHashCode(), FileSuffix.SUCCESS.getSuffix());
        if (filePath == null) {
            restTemplate.execute(fileDto.getUrl(), HttpMethod.GET, clientHttpRequest -> {
                clientHttpRequest.getHeaders().set("token", token);
            }, clientHttpResponse -> {
                try (BufferedInputStream bis = new BufferedInputStream(clientHttpResponse.getBody())) {
                    int count;
                    byte[] bytes = new byte[1024];
                    while ((count = bis.read(bytes, 0, 1024)) != -1) {
                        zipStream.write(bytes, 0, count);
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
                return true;
            });
        } else {
            try (InputStream stream = new FileInputStream(new File(filePath + File.separator + fileDto.getHashCode() + FileSuffix.SUCCESS.getSuffix()));
                 BufferedInputStream bis = new BufferedInputStream(stream)) {
                byte[] bytes = new byte[1024];
                int count;
                while ((count = bis.read(bytes, 0, 1024)) != -1) {
                    zipStream.write(bytes, 0, count);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }


}
